﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Singer.Core;
using Singer.Shared.Domain;
using Singer.Shared.Modularity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Singer.Middleware.EFCore
{
    public class EFCoreDbContext : DbContext
    {
        private readonly IServiceProvider ServiceProvider;
        private readonly IUserContextAccessor? _userContextAccessor;
        private readonly EFCoreOptions _efoptions;
        public EFCoreDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(options)
        {
            ServiceProvider = serviceProvider;
            _userContextAccessor = serviceProvider.GetService<IUserContextAccessor>();
            _efoptions = serviceProvider.GetRequiredService<IOptionsSnapshot<EFCoreOptions>>().Value;
            if (_efoptions.UseAopAudit)
                SavingChanges += EFCoreDbContext_SavingChanges;
        }

        private void EFCoreDbContext_SavingChanges(object? sender, SavingChangesEventArgs e)
        {
            IUserContext? _userContext = _userContextAccessor?.UserContext;
            foreach (var entry in ChangeTracker.Entries())
            {
                if (entry.State == EntityState.Added)
                {
                    if (entry.Entity is ICreator creator)
                    {
                        if (string.IsNullOrWhiteSpace(creator.CreatorId) && !string.IsNullOrWhiteSpace(_userContext?.UserId))
                            entry.Entity.GetType().GetProperty(nameof(creator.CreatorId))?.SetValue(entry.Entity, _userContext.UserId);
                        if (creator.CreateTime == DateTime.MinValue)
                            entry.Entity.GetType().GetProperty(nameof(creator.CreateTime))?.SetValue(entry.Entity, Cores.BeiJingNow);
                    }
                }
                else if (entry.State == EntityState.Modified)
                {
                    if (entry.Entity is IChanger changer)
                    {
                        if (!string.IsNullOrWhiteSpace(_userContext?.UserId))
                            entry.Entity.GetType().GetProperty(nameof(changer.ChangerId))?.SetValue(entry.Entity, _userContext.UserId);
                        entry.Entity.GetType().GetProperty(nameof(changer.ChangeTime))?.SetValue(entry.Entity, Cores.BeiJingNow);
                    }
                }
                else if (entry.State == EntityState.Deleted)
                {
                    if (entry.Entity is ISoftDelete deleter)
                    {
                        entry.State = EntityState.Modified;
                        if (!string.IsNullOrWhiteSpace(_userContext?.UserId))
                            entry.Entity.GetType().GetProperty(nameof(deleter.DeleterId))?.SetValue(entry.Entity, _userContext.UserId);
                        entry.Entity.GetType().GetProperty(nameof(deleter.IsDeleted))?.SetValue(entry.Entity, true);
                        entry.Entity.GetType().GetProperty(nameof(deleter.DeleteTime))?.SetValue(entry.Entity, Cores.BeiJingNow);
                    }
                }

            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            ApplySoftDeleteGlobalFilter(modelBuilder);
        }

        /// <summary>
        /// 过滤掉全部软删除的数据
        /// </summary>
        private void ApplySoftDeleteGlobalFilter(ModelBuilder modelBuilder)
        {
            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
                {
                    var parameter = Expression.Parameter(entityType.ClrType, "entity");
                    var propertyMethodInfo = typeof(EF).GetMethod("Property").MakeGenericMethod(typeof(bool));
                    var isDeletedProperty = Expression.Call(propertyMethodInfo, parameter, Expression.Constant(nameof(ISoftDelete.IsDeleted)));
                    var compareExpression = Expression.MakeBinary(ExpressionType.Equal, isDeletedProperty, Expression.Constant(false));
                    var lambda = Expression.Lambda(compareExpression, parameter);
                    modelBuilder.Entity(entityType.ClrType).HasQueryFilter(lambda);
                }
            }
        }
    }
}
